<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.0.2">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
  
  <link rel="stylesheet" href="/lib/animate-css/animate.min.css">

<script class="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"jiapengcai.gitee.io","root":"/","scheme":"Gemini","version":"8.0.0-rc.5","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":true,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":"valine","storage":true,"lazyload":false,"nav":null,"activeClass":"valine"},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false};
  </script>

  <meta name="description" content="Remote procedure call (RPC，远程过程调用)In the second tutorial we learned how to use Work Queues to distribute time-consuming tasks among multiple workers. 在教程2里我们学习了如何使用工作队列在多个工作者之间分发耗时任务。">
<meta property="og:type" content="article">
<meta property="og:title" content="RabbitMQ Spring-AMQP官方教程（六）--RPC">
<meta property="og:url" content="https://jiapengcai.gitee.io/posts/40514/index.html">
<meta property="og:site_name" content="PenG">
<meta property="og:description" content="Remote procedure call (RPC，远程过程调用)In the second tutorial we learned how to use Work Queues to distribute time-consuming tasks among multiple workers. 在教程2里我们学习了如何使用工作队列在多个工作者之间分发耗时任务。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://www.rabbitmq.com/img/tutorials/python-six.png">
<meta property="article:published_time" content="2018-05-17T14:21:11.000Z">
<meta property="article:modified_time" content="2019-08-17T02:52:02.386Z">
<meta property="article:author" content="Jiapeng Cai">
<meta property="article:tag" content="翻译">
<meta property="article:tag" content="编程">
<meta property="article:tag" content="RabbitMQ">
<meta property="article:tag" content="Spring AMQP">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://www.rabbitmq.com/img/tutorials/python-six.png">

<link rel="canonical" href="https://jiapengcai.gitee.io/posts/40514/">


<script class="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>RabbitMQ Spring-AMQP官方教程（六）--RPC | PenG</title>
  






  <noscript>
  <style>
  body { margin-top: 2rem; }

  .use-motion .menu-item,
  .use-motion .sidebar,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header {
    visibility: visible;
  }

  .use-motion .header,
  .use-motion .site-brand-container .toggle,
  .use-motion .footer { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle,
  .use-motion .custom-logo-image {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line {
    transform: scaleX(1);
  }

  .search-pop-overlay, .sidebar-nav { display: none; }
  .sidebar-panel { display: block; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <main class="main">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader">
        <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">PenG</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
  </ul>
</nav>




</div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <section class="post-toc-wrap sidebar-panel">
          <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#Remote-procedure-call-RPC%EF%BC%8C%E8%BF%9C%E7%A8%8B%E8%BF%87%E7%A8%8B%E8%B0%83%E7%94%A8"><span class="nav-number">1.</span> <span class="nav-text">Remote procedure call (RPC，远程过程调用)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Client-interface%EF%BC%88%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%8F%A3%EF%BC%89"><span class="nav-number">1.0.1.</span> <span class="nav-text">Client interface（客户端接口）</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#A-note-on-RPC%EF%BC%88RPC%E7%9A%84%E6%B3%A8%E6%84%8F%E7%82%B9%EF%BC%89"><span class="nav-number">1.0.1.1.</span> <span class="nav-text">A note on RPC（RPC的注意点）</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Callback-queue%EF%BC%88%E5%9B%9E%E8%B0%83%E9%98%9F%E5%88%97%EF%BC%89"><span class="nav-number">1.0.2.</span> <span class="nav-text">Callback queue（回调队列）</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#Message-properties%EF%BC%88%E6%B6%88%E6%81%AF%E5%B1%9E%E6%80%A7%EF%BC%89"><span class="nav-number">1.0.2.1.</span> <span class="nav-text">Message properties（消息属性）</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Correlation-Id%EF%BC%88%E5%85%B3%E8%81%94Id%EF%BC%89"><span class="nav-number">1.0.3.</span> <span class="nav-text">Correlation Id（关联Id）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Summary%EF%BC%88%E6%80%BB%E7%BB%93%EF%BC%89"><span class="nav-number">1.0.4.</span> <span class="nav-text">Summary（总结）</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Putting-it-all-together%EF%BC%88%E4%BB%A3%E7%A0%81%E6%95%B4%E5%90%88%EF%BC%89"><span class="nav-number">1.1.</span> <span class="nav-text">Putting it all together（代码整合）</span></a></li></ol></li></ol></div>
      </section>
      <!--/noindex-->

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Jiapeng Cai"
      src="/images/avatar.png">
  <p class="site-author-name" itemprop="name">Jiapeng Cai</p>
  <div class="site-description" itemprop="description">Life can be programmed.</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives">
          <span class="site-state-item-count">9</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">3</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">7</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/jiapengcai" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;jiapengcai" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:635793@gmail.com" title="E-Mail → mailto:635793@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



      </section>
    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </header>

      
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>
  <a role="button" class="book-mark-link book-mark-link-fixed"></a>

  <a href="https://github.com/jiapengcai" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>

<noscript>
  <div id="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


      <div class="main-inner">
        

        <div class="content post posts-expand">
          

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://jiapengcai.gitee.io/posts/40514/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.png">
      <meta itemprop="name" content="Jiapeng Cai">
      <meta itemprop="description" content="Life can be programmed.">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="PenG">
    </span>

    
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          RabbitMQ Spring-AMQP官方教程（六）--RPC
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2018-05-17 22:21:11" itemprop="dateCreated datePublished" datetime="2018-05-17T22:21:11+08:00">2018-05-17</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2019-08-17 10:52:02" itemprop="dateModified" datetime="2019-08-17T10:52:02+08:00">2019-08-17</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E7%BF%BB%E8%AF%91/" itemprop="url" rel="index"><span itemprop="name">翻译</span></a>
                </span>
            </span>

          
            <span id="/posts/40514/" class="post-meta-item leancloud_visitors" data-flag-title="RabbitMQ Spring-AMQP官方教程（六）--RPC" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/posts/40514/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/posts/40514/" itemprop="commentCount"></span>
    </a>
  </span>
  
  

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="Remote-procedure-call-RPC，远程过程调用"><a href="#Remote-procedure-call-RPC，远程过程调用" class="headerlink" title="Remote procedure call (RPC，远程过程调用)"></a>Remote procedure call (RPC，远程过程调用)</h1><p>In the second tutorial we learned how to use <em>Work Queues</em> to distribute time-consuming tasks among multiple workers.</p>
<p>在教程2里我们学习了如何使用工作队列在多个工作者之间分发耗时任务。</p>
<a id="more"></a>

<p>But what if we need to run a function on a remote computer and wait for the result? Well, that’s a different story. This pattern is commonly known as <em>Remote Procedure Call or RPC</em>.</p>
<p>但如果我们需要在一个远程电脑上运行一个函数并且等待运行结果的话要怎么办呢？这就变成另一个问题了。这种模式通常被称为远程过程调用（Remote Procedure Call），或者简称RPC。</p>
<p>In this tutorial we’re going to use RabbitMQ to build an RPC system: a client and a scalable RPC server. As we don’t have any time-consuming tasks that are worth distributing, we’re going to create a dummy RPC service that returns Fibonacci numbers.</p>
<p>在本节教程里，我们将用RabbitMQ来构建一个RPC系统，这个系统包括一个客户端和一个可伸缩的RPC服务端。由于我们没有什么耗时任务值得分发，所以我们准备创建一个假的RPC服务，这个服务返回斐波那契（Fibonacci）数值。</p>
<h3 id="Client-interface（客户端接口）"><a href="#Client-interface（客户端接口）" class="headerlink" title="Client interface（客户端接口）"></a>Client interface（客户端接口）</h3><p>To illustrate how an RPC service could be used we’re going to change the names of our profiles from “Sender” and “Receiver” to “Client” and “Server”. When we call the server we will get back the fibonacci of the argument we call with.</p>
<p>为了说明RPC服务可以如何被使用，我们准备修改我们的配置组，将名称从“Sender”和“Receiver”换成“Client”和“Server”。当我们调用服务端时，我们将会获得我们传入的参数所对应的斐波那契数值。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Integer response = (Integer) template.convertSendAndReceive</span><br><span class="line">(exchange.getName(), <span class="string">&quot;rpc&quot;</span>, start++);</span><br><span class="line">System.out.println(<span class="string">&quot; [.] Got &#x27;&quot;</span> + response + <span class="string">&quot;&#x27;&quot;</span>);</span><br></pre></td></tr></table></figure>

<blockquote>
<h4 id="A-note-on-RPC（RPC的注意点）"><a href="#A-note-on-RPC（RPC的注意点）" class="headerlink" title="A note on RPC（RPC的注意点）"></a>A note on RPC（RPC的注意点）</h4><p>Although RPC is a pretty common pattern in computing, it’s often criticised. The problems arise when a programmer is not aware whether a function call is local or if it’s a slow RPC. Confusions like that result in an unpredictable system and adds unnecessary complexity to debugging. Instead of simplifying software, misused RPC can result in unmaintainable spaghetti code.</p>
<p>虽然RPC在计算领域是很常见的模式，但它通常也是受争议的。但程序员不知道一个函数调用是本地的还是慢速的RPC时就会出现一些问题。像这样的混乱会导致不可预知的系统，而且会给调试增加不必要的复杂性。不恰当地使用RPC不仅不会简化程序，还会导致代码变得很难维护。</p>
<p>Bearing that in mind, consider the following advice:</p>
<p>记住这一点，然后考虑一下几点建议：</p>
<ul>
<li>Make sure it’s obvious which function call is local and which is remote.（确保哪个函数调用是本地的，哪个是远程的。）</li>
<li>Document your system. Make the dependencies between components clear.（为你的系统做好文档。清晰化组件间的依赖。）</li>
<li>Handle error cases. How should the client react when the RPC server is down for a long time?（处理好会发生错误的场景。但RPC服务端长时间挂掉时，客户端应该做出什么反应？）</li>
</ul>
<p>When in doubt avoid RPC. If you can, you should use an asynchronous pipeline - instead of RPC-like blocking, results are asynchronously pushed to a next computation stage.</p>
<p>当你无法对这些问题无法做出明确回答时，就不要使用RPC。如果可以的话，你应该使用异步pipeline，而不是类似于阻塞的RPC。使用异步pipeline，计算结果可以异步推入到下一个计算阶段。</p>
</blockquote>
<h3 id="Callback-queue（回调队列）"><a href="#Callback-queue（回调队列）" class="headerlink" title="Callback queue（回调队列）"></a>Callback queue（回调队列）</h3><p>In general doing RPC over RabbitMQ is easy. A client sends a request message and a server replies with a response message. In order to receive a response we need to send a ‘callback’ queue address with the request. Spring-amqp’s RabbitTemplate handles the callback queue for us when we use the above ‘convertSendAndReceive()‘ method. There is no need to do any other setup when using the RabbitTemplate. For a thorough explanation please see <a target="_blank" rel="noopener" href="http://docs.spring.io/spring-amqp/reference/htmlsingle/#request-reply">Request/Reply Message</a>.</p>
<p>一般情况下，在RabbitMQ上实现RPC挺简单的。客户端发送请求消息然后服务端返回一个响应消息。为了接收响应消息，我们必须传送一个用于处理请求的回调队列。在我们使用“convertSendAndReceive()”方法时，Spring-amqp框架的RabbitTemplate类为我们做好了回调队列的处理工作。使用RabbitTemplate类时无需在做其它配置。若想看完整的文档，请参阅<a target="_blank" rel="noopener" href="https://legacy.gitbook.com/book/jiapengcai/rabbitmq/edit#">请求/发送消息</a>。</p>
<blockquote>
<h4 id="Message-properties（消息属性）"><a href="#Message-properties（消息属性）" class="headerlink" title="Message properties（消息属性）"></a>Message properties（消息属性）</h4><p>The AMQP 0-9-1 protocol predefines a set of 14 properties that go with a message. Most of the properties are rarely used, with the exception of the following:</p>
<p>AMQP 0-9-1协议预定义了14个消息属性。大部分的属性都很少用到，除了以下几个：</p>
<ul>
<li>deliveryMode: Marks a message as persistent (with a value of 2) or transient (any other value). You may remember this property from the second tutorial.</li>
<li>deliveryMode：将消息标记为要持久化（此时属性值为2）或者瞬态（此时属性值为2以外的其它数字）。教程2里提到过这<br>个属性，你应该还记得。</li>
<li>contentType: Used to describe the mime-type of the encoding. For example for the often used JSON encoding it is a good practice to set this property to: application/json.</li>
<li>contentType：用来描述编码的mime类型。例如，对于常用的JSON格式，最好将这个属性值设为application/json。</li>
<li>replyTo: Commonly used to name a callback queue.</li>
<li>replayTo：通常用来命名一个回调队列。</li>
<li>correlationId: Useful to correlate RPC responses with requests.</li>
<li>correlationId：该属性用来将RPC响应与请求进行关联。</li>
</ul>
</blockquote>
<h3 id="Correlation-Id（关联Id）"><a href="#Correlation-Id（关联Id）" class="headerlink" title="Correlation Id（关联Id）"></a>Correlation Id（关联Id）</h3><p>Spring-amqp allows you to focus on the message style you’re working with and hide the details of message plumbing required to support this style. For example, typically the native client would create a callback queue for every RPC request. That’s pretty inefficient so an alternative is to create a single callback queue per client.</p>
<p>Spring-amqp能让你专注于正在处理的消息类型，并隐藏了支持该类型的消息所需的消息管道的实现细节。例如，通常情况下，本地客户端会为每个RPC请求都创建一个回调队列。这种做法效率很低，所以替换方案是每个客户端只创建一个回调队列。</p>
<p>That raises a new issue, having received a response in that queue it’s not clear to which request the response belongs. That’s when the correlationId property is used. Spring-amqp automatically sets a unique value for every request. In addition it handles the details of matching the response with the correct correlationId.</p>
<p>但这会导致一个新的问题，那就是，对于从这个队列里接收的响应，我们无法知道它对应的是哪个请求。这时候，correlationId就派上用场了。spring-amqp自动帮我们为每一个请求设好了唯一的correlationId值。而且，它还帮我们做好了将响应与correlationId进行匹配的细节。</p>
<p>One reason that spring-amqp makes rpc style easier is that sometimes you may want to ignore unknown messages in the callback queue, rather than failing with an error. It’s due to a possibility of a race condition on the server side. Although unlikely, it is possible that the RPC server will die just after sending us the answer, but before sending an acknowledgment message for the request. If that happens, the restarted RPC server will process the request again. The spring-amqp client handles the duplicate responses gracefully, and the RPC should ideally be idempotent.</p>
<p>spring-amqp使得rpc模式变得简单的一个原因是，有时你可能会想忽略回调队列里的一些未知消息，而不是抛出错误。这是因为服务端可能会出现竞争的情况。有可能RPC服务端在给我们发送完响应但却还没来得及发送确认消息时，它就挂了，虽然看起来不大像会这样。如果发生了这种情况，重启RPC服务端会继续再去处理这条请求。spring-amqp客户端会优雅地处理重复的响应，这种情况下，RPC应该是完美幂等的。</p>
<h3 id="Summary（总结）"><a href="#Summary（总结）" class="headerlink" title="Summary（总结）"></a>Summary（总结）</h3><p><img src="https://www.rabbitmq.com/img/tutorials/python-six.png"></p>
<p>Our RPC will work like this:</p>
<p>我们的RPC系统</p>
<p>1.The Tut6Config will setup a new DirectExchange and a client</p>
<p>在Tut6Config文件里将建立一个新的DirectExchange和一个客户端。</p>
<p>2.The client will leverage the convertSendAndReceive passing the exchange name, the routingKey, and the message.</p>
<p>客户端将使用convertSendAndReceive，并传入交换器名字，路由键和消息。</p>
<p>3.The request is sent to an rpc_queue(“tut.rpc”) queue.</p>
<p>请求被发送到用于rpc的队列里（“tut.rpc”）。</p>
<p>4.The RPC worker (aka: server) is waiting for requests on that queue. When a request appears, it performs the task and sends a message with the result back to the Client, using the queue from the replyTo field.</p>
<p>RPC工作者（也就是服务器）等待发送到队列里的请求。但一个请求出现时，它就执行任务，然后通过使用replyTo域里配置的队列将带有结果的消息发回给客户端。</p>
<p>5.The client waits for data on the callback queue. When a message appears, it checks the correlationId property. If it matches the value from the request it returns the response to the application. Again, this is done automagically via the RabbitTemplate.</p>
<p>客户端等待回调队列里的数据。当一条消息出现时，它会校验correlationId属性。如果属性值与请求匹配，它就将响应返回给应用。这个工作RabbitTemplate自动帮我们完成了。</p>
<h2 id="Putting-it-all-together（代码整合）"><a href="#Putting-it-all-together（代码整合）" class="headerlink" title="Putting it all together（代码整合）"></a>Putting it all together（代码整合）</h2><p>The Fibonacci task is a @RabbitListener and is defined as:</p>
<p>计算斐波那契的任务用@RabbitListener进行标注，任务内容的定义如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">fib</span><span class="params">(<span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> n == <span class="number">0</span> ? <span class="number">0</span> : n == <span class="number">1</span> ? <span class="number">1</span> : (fib(n - <span class="number">1</span>) + fib(n - <span class="number">2</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>We declare our fibonacci function. It assumes only valid positive integer input. (Don’t expect this one to work for big numbers, and it’s probably the slowest recursive implementation possible).</p>
<p>我们声明了斐波那契函数。它假定输入的参数是有效的正整数。（不要期望它能用于大数的场景，而且这种方式是最低效的递归实现）。</p>
<p>The code for our <a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/spring-amqp/src/main/java/org/springframework/amqp/tutorials/tut6/Tut6Config.java">Tut6Config</a> looks like this:</p>
<p><a target="_blank" rel="noopener" href="https://legacy.gitbook.com/book/jiapengcai/rabbitmq/edit#">Tut6Config</a>的代码看起来是如下这样子的：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.amqp.core.Binding;</span><br><span class="line"><span class="keyword">import</span> org.springframework.amqp.core.BindingBuilder;</span><br><span class="line"><span class="keyword">import</span> org.springframework.amqp.core.DirectExchange;</span><br><span class="line"><span class="keyword">import</span> org.springframework.amqp.core.Queue;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Profile;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Profile(&#123;&quot;tut6&quot;,&quot;rpc&quot;&#125;)</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tut6Config</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Profile(&quot;client&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientConfig</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> DirectExchange <span class="title">exchange</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> DirectExchange(<span class="string">&quot;tut.rpc&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Tut6Client <span class="title">client</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Tut6Client();</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Profile(&quot;server&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ServerConfig</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Queue <span class="title">queue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Queue(<span class="string">&quot;tut.rpc.requests&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> DirectExchange <span class="title">exchange</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> DirectExchange(<span class="string">&quot;tut.rpc&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Binding <span class="title">binding</span><span class="params">(DirectExchange exchange, Queue queue)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> BindingBuilder.bind(queue).to(exchange).with(<span class="string">&quot;rpc&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Tut6Server <span class="title">server</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> Tut6Server();</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>It setups up our profiles as “tut6” or “rpc”. It also setups a “client” profile with two beans; 1) the DirectExchange we are using and 2) the Tut6Client itself. We also configure the “server” profile with three beans, the “tut.rpc.requests” queue, the DirectExchange, which matches the client’s exchange, and the binding from the queue to the exchange with the “rpc” routing-key.</p>
<p>它建立了我们的配置组，叫“tut6”或者“rpc”。同时，还建立了一个“client”配置组，这个组里配置了两个bean：一个是我们将要用到的DirectExchange类型的交换器，一个是Tut6Client本身。我们还建立了一个“server”配置组，这个组里配置了三个bean：一个名为“tut.rpc.requests”的队列，一个与客户端交换器相匹配的DirectExchange类型的交换器，以及用名为“rpc”的路由键将队列和交换器的绑定器。</p>
<p>The server code is rather straightforward:</p>
<p>服务端代码更直观点：</p>
<p>1.As usual we start annotating our receiver method with a @RabbitListener and defining the queue its listening on.</p>
<p>像之前那样，我们先用@RabbitListener来注解我们的接收者方法，然后定义它要监听的队列。</p>
<p>2.Our fibanacci method calls fib() with the payload parameter and returns the result.</p>
<p>我们的斐波那契方法被命名为fib()，接收有效参数并返回结果。</p>
<p>The code for our RPC server <a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/spring-amqp/src/main/java/org/springframework/amqp/tutorials/tut6/Tut6Server.java">Tut6Server.java</a>:</p>
<p>以下为我们的RPC服务端代码<a target="_blank" rel="noopener" href="https://legacy.gitbook.com/book/jiapengcai/rabbitmq/edit#">Tut6Server.java</a>:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> org.springframework.amqp.tutorials.tut6;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.amqp.rabbit.annotation.RabbitListener;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tut6Server</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RabbitListener(queues = &quot;tut.rpc.requests&quot;)</span></span><br><span class="line">    <span class="comment">// @SendTo(&quot;tut.rpc.replies&quot;) used when the</span></span><br><span class="line">    <span class="comment">// client doesn&#x27;t set replyTo.</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">fibonacci</span><span class="params">(<span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; [x] Received request for &quot;</span> + n);</span><br><span class="line">        <span class="keyword">int</span> result = fib(n);</span><br><span class="line">        System.out.println(<span class="string">&quot; [.] Returned &quot;</span> + result);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">fib</span><span class="params">(<span class="keyword">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> n == <span class="number">0</span> ? <span class="number">0</span> : n == <span class="number">1</span> ? <span class="number">1</span> : (fib(n - <span class="number">1</span>) + fib(n - <span class="number">2</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>The client code <a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/spring-amqp/src/main/java/org/springframework/amqp/tutorials/tut6/Tut6Client.java">Tut6Client</a> is as easy as the server:</p>
<p>客户端代码<a target="_blank" rel="noopener" href="https://legacy.gitbook.com/book/jiapengcai/rabbitmq/edit#">Tut6Client</a>与服务端代码一样简单：</p>
<p>1.We autowire the RabbitTemplate and the DirectExchange bean as defined in the Tut6Config.</p>
<p>我们自动注入Tut6Config里定义的类型为RabbitTemplate和DirectExchange的bean。</p>
<p>2.We invoke template.convertSendAndReceive with the parameters exchange name, routing key and message.</p>
<p>我们调用template.convertSendAndReceive，传入的参数为交换器名字，路由键以及消息。</p>
<p>3.We print the result.</p>
<p>打印出结果。</p>
<p>Making the Client request is simply:</p>
<p>发起客户端请求也很简单：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.amqp.core.DirectExchange;</span><br><span class="line"><span class="keyword">import</span> org.springframework.amqp.rabbit.core.RabbitTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.annotation.Scheduled;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tut6Client</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> RabbitTemplate template;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> DirectExchange exchange;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">int</span> start = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Scheduled(fixedDelay = 1000, initialDelay = 500)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">send</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot; [x] Requesting fib(&quot;</span> + start + <span class="string">&quot;)&quot;</span>);</span><br><span class="line">        Integer response = (Integer) template.convertSendAndReceive(exchange.getName(), <span class="string">&quot;rpc&quot;</span>, start++);</span><br><span class="line">        System.out.println(<span class="string">&quot; [.] Got &#x27;&quot;</span> + response + <span class="string">&quot;&#x27;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Using the project setup as defined in (see tutorial one) with start.spring.io and SpringInitialzr the preparing the runtime is the same as the other tutorials:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mvn clean package</span><br></pre></td></tr></table></figure>

<p>We can start the server with:</p>
<p>我们可以用以下命令来启动服务端：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">java -jar target&#x2F;rabbit-tutorials-1.7.1.RELEASE.jar</span><br><span class="line">--spring.profiles.active&#x3D;rpc,server</span><br><span class="line">--tutorial.client.duration&#x3D;6000</span><br></pre></td></tr></table></figure>

<p>To request a fibonacci number run the client:</p>
<p>若要请求斐波那契数值，可以用以下命令来启动客户端：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">java -jar target&#x2F;rabbit-tutorials-1.7.1.RELEASE.jar</span><br><span class="line">--spring.profiles.active&#x3D;rpc,server</span><br><span class="line">java -jar target&#x2F;rabbit-tutorials-1.7.1.RELEASE.jar</span><br><span class="line">--spring.profiles.active&#x3D;rpc,client</span><br></pre></td></tr></table></figure>

<p>The design presented here is not the only possible implementation of a RPC service, but it has some important advantages:</p>
<p>以上展示的设计不仅仅是实现RPC服务的一种方式，而且还有一些重要的优点：</p>
<p>1.If the RPC server is too slow, you can scale up by just running another one. Try running a second RPC Server in a new console.</p>
<p>如果RPC服务端太慢，你可以仅仅通过额外再运行一个服务端来增大其规模。可以尝试着在新的控制台里运行第二个RPC服务端。</p>
<p>2.On the client side, the RPC requires sending and receiving only one message with one method. No synchronous calls like queueDeclare are required. As a result the RPC client needs only one network round trip for a single RPC request.</p>
<p>在客户端这一边，RPC要求只能一个方法里发送和接收一条消息。不需要像queueDelare那样的同步调用。于是，对于一个RPC请求，RPC客户端只需一个网络回路。</p>
<p>Our code is still pretty simplistic and doesn’t try to solve more complex (but important) problems, like:</p>
<p>我们的代码仍然很简单，而且没有处理很多复杂（但重要）的问题，像：</p>
<p>1.How should the client react if there are no servers running?</p>
<p>如果没有服务端正在运行，客户端应该做出什么反应？</p>
<p>2.Should a client have some kind of timeout for the RPC?</p>
<p>客户端需要为RPC做超时处理吗？</p>
<p>3.If the server malfunctions and raises an exception, should it be forwarded to the client?</p>
<p>如果服务端出现故障并引起了异常，它是否应该告知客户端？</p>
<p>4.Protecting against invalid incoming messages (eg checking bounds, type) before processing.</p>
<p>在处理进来的消息之前先处理掉无效的消息（如校验边界，类型）。</p>
<blockquote>
<p>If you want to experiment, you may find the <a target="_blank" rel="noopener" href="https://www.rabbitmq.com/management.html">management UI</a> useful for viewing the queues.</p>
<p>如果你想做些实验，可以通过<a target="_blank" rel="noopener" href="https://legacy.gitbook.com/book/jiapengcai/rabbitmq/edit#">管理界面</a>来查看队列，你会发现它很有用的。</p>
</blockquote>
<p>There is one other nice feature of RabbitMQ. It is featured as a supported tile on Pivotal Cloud Foundry (PCF) as a service.</p>
<p>RabbitMQ还有另一个很棒的特性。它作为服务在Pivotal Cloud Foundry（PCF）上被支持。</p>

    </div>

    
    
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Jiapeng Cai
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://jiapengcai.gitee.io/posts/40514/" title="RabbitMQ Spring-AMQP官方教程（六）--RPC">https://jiapengcai.gitee.io/posts/40514/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E7%BF%BB%E8%AF%91/" rel="tag"><i class="fa fa-tag"></i> 翻译</a>
              <a href="/tags/%E7%BC%96%E7%A8%8B/" rel="tag"><i class="fa fa-tag"></i> 编程</a>
              <a href="/tags/RabbitMQ/" rel="tag"><i class="fa fa-tag"></i> RabbitMQ</a>
              <a href="/tags/Spring-AMQP/" rel="tag"><i class="fa fa-tag"></i> Spring AMQP</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/posts/2339/" rel="prev" title="RabbitMQ Spring-AMQP官方教程（五）--主题">
      <i class="fa fa-chevron-left"></i> RabbitMQ Spring-AMQP官方教程（五）--主题
    </a></div>
      <div class="post-nav-item">
    <a href="/posts/42624/" rel="next" title="MySQL函数GROUP_CONCAT的使用">
      MySQL函数GROUP_CONCAT的使用 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



        </div>
        
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fab fa-affiliatetheme"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Jiapeng Cai</span>
</div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/next-boot.js"></script>

<script src="/js/bookmark.js"></script>


  















  

  

  


<script>
NexT.utils.loadComments('#valine-comments', () => {
  NexT.utils.getScript('//cdn.jsdelivr.net/npm/valine@1/dist/Valine.min.js', () => {
    new Valine(Object.assign({
      el  : '#valine-comments',
      path: location.pathname,
    }, {"enable":true,"appId":"9R584ubklhG3KtIebsybj2Fg-gzGzoHsz","appKey":"hTQwNb5tpJrtED29EvBeAshK","placeholder":"Just go go","avatar":"mm","meta":["nick","mail","link"],"pageSize":10,"language":"zh-cn","visitor":true,"comment_count":true,"recordIP":false,"serverURLs":null,"enableQQ":false,"requiredFields":[]}
    ));
  }, window.Valine);
});
</script>

</body>
</html>
